﻿<template>
    <div>
        <h2>IIS日志统计分析</h2>
        <div style="margin-bottom: 16px; display: flex; align-items: center;">
            <label for="host-select" style="font-weight: bold; margin-right: 8px;">主机筛选：</label>
            <select id="host-select" v-model="selectedHost" @change="reloadData" style="padding: 6px 12px; border-radius: 4px;">
                <option value="">全部主机</option>
                <option v-for="host in hostList" :key="host" :value="host">{{ host }}</option>
            </select>
        </div>
        <!-- 第一排：访问时间分布独占全宽 -->
        <div class="row">
            <section class="full-chart" v-if="charts.length > 0 && charts[0]">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <h3 style="margin: 0;">{{ charts[0].title }}</h3>
                    <button class="more-btn" @click="showMore(0)">更多</button>
                </div>
                <v-chart :option="charts[0].option" autoresize style="height:400px;width:100%;" />
            </section>
        </div>
        <!-- 第二排开始 -->
        <div class="row">
            <section v-for="(chart, idx) in charts.slice(1)" :key="idx+1">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <h3 style="margin: 0;">{{ chart.title }}</h3>
                    <button class="more-btn" @click="showMore(idx+1)">更多</button>
                </div>
                <v-chart :option="chart.option" autoresize style="height:400px" />
            </section>
        </div>
        <!-- 弹窗对话框 -->
        <div v-if="dialogVisible" class="dialog-mask" @click.self="dialogVisible = false">
            <div class="dialog-panel">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <h3 style="margin: 0;">{{ charts[dialogIndex].title }} - 全部数据</h3>
                    <button class="close-btn" @click="dialogVisible = false">关闭</button>
                </div>
                <div class="dialog-content">
                    <table>
                        <thead>
                            <tr>
                                <th>名称</th>
                                <th>访问量</th>
                                <th>IP量</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr v-for="row in charts[0].allData" :key="row.key" v-if="dialogIndex === 0">
                                <td>{{ row.key }}</td>
                                <td>{{ row.value }}</td>
                                <td>{{ row.ipCount }}</td>
                            </tr>
                            <tr v-for="row in charts[dialogIndex].allData" :key="row.key" v-else>
                                <td>{{ row.key }}</td>
                                <td>{{ row.value }}</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</template>

<script lang="ts" setup>
    import { ref, onMounted } from 'vue'
    import VChart from 'vue-echarts'
    import { use } from 'echarts/core'
    import { CanvasRenderer } from 'echarts/renderers'
    import { BarChart, PieChart, LineChart } from 'echarts/charts'
    import {
        TitleComponent,
        TooltipComponent,
        LegendComponent,
        GridComponent,
    } from 'echarts/components'
    import { fetchAnalyze, AnalyzeData } from '../api/analyze'

    use([
        CanvasRenderer,
        BarChart,
        PieChart,
        LineChart,
        TitleComponent,
        TooltipComponent,
        LegendComponent,
        GridComponent,
    ])

    function getTop(data: { key: string; value: number }[], count = 10) {
        return [...data].sort((a, b) => b.value - a.value).slice(0, count)
    }

    // 获取完整24小时分布，自动补0
    function get24Hours(data: { key: string; value: number }[]) {
        if (!data.length) return Array.from({ length: 24 }, (_, h) => ({ key: `${new Date().toISOString().slice(0, 10)} ${h.toString().padStart(2, '0')}`, value: 0 }))
        const days = Array.from(new Set(data.map(x => x.key.substring(0, 10)))).sort().reverse()
        const targetDay = days[0]
        const hourMap = Object.fromEntries(data.filter(x => x.key.startsWith(targetDay)).map(x => [x.key.slice(11, 13), x.value]))
        return Array.from({ length: 24 }, (_, h) => {
            const hourStr = h.toString().padStart(2, '0')
            return {
                key: `${targetDay} ${hourStr}`,
                value: hourMap[hourStr] ?? 0
            }
        })
    }

    // 获取每小时唯一IP数（后端已提供 hourlyIpDistribution）
    function get24HourUniqueIPs(timeDist, hourlyIpDistribution) {
        const ipMap = Object.fromEntries(hourlyIpDistribution.map(x => [x.hour.slice(11), x.ips.length]))
        return timeDist.map(i => ipMap[i.key.slice(11)] ?? 0)
    }

    const charts = ref<any[]>([])
    const dialogVisible = ref(false)
    const dialogIndex = ref(0)
    const hostList = ref<string[]>([])
    const selectedHost = ref<string>('')

    function showMore(idx: number) {
        dialogIndex.value = idx
        dialogVisible.value = true
    }

    async function loadHosts(file: string = 'iis.log') {
        const data: AnalyzeData = await fetchAnalyze(file, "")
        hostList.value = [...new Set(data.domains.map((x: { key: string }) => x.key))]
    }

    async function reloadData() {
        const file = 'iis.log'
        const host = selectedHost.value
        const data: AnalyzeData = await fetchAnalyze(file, host)
        const timeDist = get24Hours(data.timeDistribution)
        // 用后端的 hourlyIpDistribution 数据
        const ipCounts = get24HourUniqueIPs(timeDist, data.hourlyIpDistribution || [])

        // 1. 语言编码与国家名映射表
        const langToCountry = {
            "zh-CN": "中国",
            "zh-TW": "台湾",
            "en-US": "美国",
            "en-GB": "英国",
            "en-CA": "加拿大",
            "de-DE": "德国",
            "de-AT": "奥地利",
            "fr-FR": "法国",
            "es-ES": "西班牙",
            "es-AR": "阿根廷",
            "ns-CN": "中国（自定义编码）",
            "dn-SC": "塞舌尔（自定义编码）",
        };

        // 2. 拼接编码和国家名
        const topData = getTop(data.countryDistribution);
        const xData = topData.map(i => {
            const country = langToCountry[i.key] || i.key;
            return `${i.key}(${country})`;
        });
        // 2. 处理 allData，给 key 加国家名
        const allData = data.countryDistribution.map(item => ({
            ...item,
            key: `${item.key}(${langToCountry[item.key] || item.key})`
        }));

        charts.value = [
            // 访问时间分布（点线图，显示全部24小时，symbolSize大号，每点都显示，+IP量数据线）
            {
                title: '访问时间分布（24小时）',
                option: {
                    title: { text: '访问时间分布（24小时）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    legend: { data: ['访问量', 'IP量'], right: '10%' },
                    xAxis: {
                        type: 'category',
                        data: timeDist.map(i => i.key.slice(11)), // 只显示小时部分
                        axisLabel: { interval: 0 }
                    },
                    yAxis: { type: 'value' },
                    series: [
                        {
                            name: '访问量',
                            type: 'line',
                            symbol: 'circle',
                            symbolSize: 24,
                            data: timeDist.map(i => i.value),
                            itemStyle: { color: '#d48265' },
                            lineStyle: { width: 2 },
                            smooth: true
                        },
                        {
                            name: 'IP量',
                            type: 'line',
                            symbol: 'rect',
                            symbolSize: 16,
                            data: ipCounts,
                            itemStyle: { color: '#5470c6' },
                            lineStyle: { width: 2, type: 'dashed' },
                            smooth: true
                        }
                    ]
                },
                allData: timeDist.map((v, idx) => ({
                    key: v.key,
                    value: v.value,
                    ipCount: ipCounts[idx]
                })),
            },
            {
                title: '访问域名排行（Top 10）',
                option: {
                    title: { text: '访问域名排行（Top 10）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    xAxis: { type: 'category', data: getTop(data.domains).map(i => i.key) },
                    yAxis: { type: 'value' },
                    series: [{
                        name: '访问量',
                        type: 'bar',
                        data: getTop(data.domains).map(i => i.value),
                        itemStyle: { color: '#5470c6' },
                    }],
                },
                allData: [...data.domains],
            },
            {
                title: 'IP访问量（Top 10）',
                option: {
                    title: { text: 'IP访问量（Top 10）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    xAxis: { type: 'category', data: getTop(data.ips).map(i => i.key) },
                    yAxis: { type: 'value' },
                    series: [{
                        name: '访问量',
                        type: 'bar',
                        data: getTop(data.ips).map(i => i.value),
                        itemStyle: { color: '#91cc75' },
                    }],
                },
                allData: [...data.ips],
            },
            {
                title: '热门静态文件（Top 10）',
                option: {
                    title: { text: '热门静态文件（Top 10）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    xAxis: { type: 'category', data: getTop(data.staticFiles).map(i => i.key) },
                    yAxis: { type: 'value' },
                    series: [{
                        name: '访问量',
                        type: 'bar',
                        data: getTop(data.staticFiles).map(i => i.value),
                        itemStyle: { color: '#fac858' },
                    }],
                },
                allData: [...data.staticFiles],
            },
            {
                title: '热门页面（Top 10）',
                option: {
                    title: { text: '热门页面（Top 10）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    xAxis: { type: 'category', data: getTop(data.pages).map(i => i.key) },
                    yAxis: { type: 'value' },
                    series: [{
                        name: '访问量',
                        type: 'bar',
                        data: getTop(data.pages).map(i => i.value),
                        itemStyle: { color: '#ee6666' },
                    }],
                },
                allData: [...data.pages],
            },
            {
                title: '来源站点（Top 10）',
                option: {
                    title: { text: '来源站点（Top 10）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    xAxis: { type: 'category', data: getTop(data.referers).map(i => i.key) },
                    yAxis: { type: 'value' },
                    series: [{
                        name: '访问量',
                        type: 'bar',
                        data: getTop(data.referers).map(i => i.value),
                        itemStyle: { color: '#73c0de' },
                    }],
                },
                allData: [...data.referers],
            },
            {
                title: '蜘蛛抓取分布（Top 10）',
                option: {
                    title: { text: '蜘蛛抓取分布（Top 10）', left: 'center' },
                    tooltip: { trigger: 'item' },
                    legend: { orient: 'vertical', left: 'left' },
                    series: [{
                        name: '蜘蛛类型',
                        type: 'pie',
                        radius: '60%',
                        data: getTop(data.spiders).map(i => ({ value: i.value, name: i.key })),
                        emphasis: {
                            itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' }
                        },
                    }],
                },
                allData: [...data.spiders],
            },
            {
                title: '常见UserAgent（Top 10）',
                option: {
                    title: { text: '常见UserAgent（Top 10）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    xAxis: { type: 'category', data: getTop(data.userAgents).map(i => i.key) },
                    yAxis: { type: 'value' },
                    series: [{
                        name: '访问量',
                        type: 'bar',
                        data: getTop(data.userAgents).map(i => i.value),
                        itemStyle: { color: '#3ba272' },
                    }],
                },
                allData: [...data.userAgents],
            },
            {
                title: '文件流量（Top 10）',
                option: {
                    title: { text: '文件流量（Top 10）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    xAxis: { type: 'category', data: getTop(data.fileFlows).map(i => i.key) },
                    yAxis: { type: 'value', name: 'MB' },
                    series: [{
                        name: '流量(MB)',
                        type: 'bar',
                        data: getTop(data.fileFlows).map(i => Math.floor(i.value)),
                        itemStyle: { color: '#fc8452' },
                    }],
                },
                allData: [...data.fileFlows],
            },
            // 访问状态码分布 改为饼图
            {
                title: '访问状态码分布（Top 10）',
                option: {
                    title: { text: '访问状态码分布（Top 10）', left: 'center' },
                    tooltip: { trigger: 'item' },
                    legend: { orient: 'vertical', left: 'left' },
                    series: [{
                        name: '状态码',
                        type: 'pie',
                        radius: '60%',
                        data: getTop(data.statusCodes).map(i => ({ value: i.value, name: i.key })),
                        emphasis: {
                            itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' }
                        },
                    }],
                },
                allData: [...data.statusCodes],
            },
            // 访问操作系统分布 改为饼图
            {
                title: '访问操作系统分布（Top 10）',
                option: {
                    title: { text: '访问操作系统分布（Top 10）', left: 'center' },
                    tooltip: { trigger: 'item' },
                    legend: { orient: 'vertical', left: 'left' },
                    series: [{
                        name: '操作系统',
                        type: 'pie',
                        radius: '60%',
                        data: getTop(data.osDistribution).map(i => ({ value: i.value, name: i.key })),
                        emphasis: {
                            itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' }
                        },
                    }],
                },
                allData: [...data.osDistribution],
            },
            // 访问浏览器分布 改为饼图
            {
                title: '访问浏览器分布（Top 10）',
                option: {
                    title: { text: '访问浏览器分布（Top 10）', left: 'center' },
                    tooltip: { trigger: 'item' },
                    legend: { orient: 'vertical', left: 'left' },
                    series: [{
                        name: '浏览器',
                        type: 'pie',
                        radius: '60%',
                        data: getTop(data.browserDistribution).map(i => ({ value: i.value, name: i.key })),
                        emphasis: {
                            itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' }
                        },
                    }],
                },
                allData: [...data.browserDistribution],
            },
            {
                title: '访问国家分布（Top 10）',
                option: {
                    title: { text: '访问国家分布（Top 10）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    xAxis: { type: 'category', data: xData },
                    yAxis: { type: 'value' },
                    series: [{
                        name: '访问量',
                        type: 'bar',
                        data: topData.map(i => i.value),
                        itemStyle: { color: '#fc8452' },
                    }],
                },
                allData: [...allData],
            },
            {
                title: '文件夹流量分布（Top 10）',
                option: {
                    title: { text: '文件夹流量分布（Top 10）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    xAxis: { type: 'category', data: getTop(data.dataFoldersFlow).map(i => i.key) },
                    yAxis: { type: 'value', name: 'MB' },
                    series: [{
                        name: '流量(MB)',
                        type: 'bar',
                        data: getTop(data.dataFoldersFlow).map(i => Math.floor(i.value)),
                        itemStyle: { color: '#9a60b4' },
                    }],
                },
                allData: [...data.dataFoldersFlow],
            },
            {
                title: '文件夹访问量（Top 10）',
                option: {
                    title: { text: '文件夹访问量（Top 10）', left: 'center' },
                    tooltip: { trigger: 'axis' },
                    xAxis: { type: 'category', data: getTop(data.dataFolders).map(i => i.key) },
                    yAxis: { type: 'value' },
                    series: [{
                        name: '访问量',
                        type: 'bar',
                        data: getTop(data.dataFolders).map(i => i.value),
                        itemStyle: { color: '#d48265' },
                    }],
                },
                allData: [...data.dataFolders],
            },
        ]
    }

    onMounted(async () => {
        await loadHosts('iis.log')
        await reloadData()
    })
</script>

<style scoped>
    .row {
        display: flex;
        flex-wrap: wrap;
        gap: 24px;
        margin-bottom: 32px;
    }

        .row .full-chart {
            width: 100%;
            min-width: 0;
            margin-bottom: 0;
            border-radius: 8px;
            background: #fafafa;
            padding: 16px;
            box-shadow: 0 2px 8px #0001;
        }

        .row section {
            flex: 1 1 600px;
            min-width: 320px;
            background: #fafafa;
            border-radius: 8px;
            padding: 16px;
            box-shadow: 0 2px 8px #0001;
        }

    .more-btn {
        background: #eee;
        border: none;
        color: #333;
        padding: 6px 16px;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
    }

        .more-btn:hover {
            background: #ccc;
        }

    .dialog-mask {
        position: fixed;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
        background: rgba(0,0,0,0.2);
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 99;
    }

    .dialog-panel {
        background: #fff;
        border-radius: 8px;
        box-shadow: 0 2px 24px #0003;
        min-width: 400px;
        max-height: 80vh;
        padding: 24px;
        overflow: auto;
    }

    .close-btn {
        background: #eee;
        border: none;
        color: #333;
        padding: 6px 16px;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
    }

        .close-btn:hover {
            background: #ccc;
        }

    .dialog-content {
        margin-top: 18px;
        max-height: 60vh;
        overflow-y: auto;
    }

        .dialog-content table {
            width: 100%;
            border-collapse: collapse;
            font-size: 15px;
        }

        .dialog-content th, .dialog-content td {
            border-bottom: 1px solid #eee;
            padding: 6px 8px;
            text-align: left;
        }

        .dialog-content th {
            background: #fafafa;
            font-weight: 600;
        }
</style>